package cn.tedu.tmall.front.mall.service.impl;
import cn.tedu.tamll.common.enumerator.ServiceCode;
import cn.tedu.tamll.common.ex.ServiceException;
import cn.tedu.tamll.common.pojo.CurrentUser;
import cn.tedu.tamll.common.pojo.vo.PageData;
import cn.tedu.tmall.front.mall.dao.repository.IGoodsRepository;
import cn.tedu.tmall.front.mall.dao.repository.IOrderItemRepository;
import cn.tedu.tmall.front.mall.dao.repository.IOrderRepository;
import cn.tedu.tmall.front.mall.dao.repository.IUserAddressRepository;
import cn.tedu.tmall.front.mall.pojo.param.OrderAddParam;
import cn.tedu.tmall.front.mall.pojo.param.OrderGoodsAddParam;
import cn.tedu.tmall.front.mall.pojo.param.UserInfoParam;
import cn.tedu.tmall.front.mall.pojo.po.GoodsPO;
import cn.tedu.tmall.front.mall.pojo.po.OrderItemPO;
import cn.tedu.tmall.front.mall.pojo.po.OrderPO;
import cn.tedu.tmall.front.mall.pojo.po.UserAddressPO;
import cn.tedu.tmall.front.mall.pojo.vo.OrderDetailVO;
import cn.tedu.tmall.front.mall.pojo.vo.OrderGoodsItemVO;
import cn.tedu.tmall.front.mall.pojo.vo.OrderLogVO;
import cn.tedu.tmall.front.mall.pojo.vo.OrderVO;
import cn.tedu.tmall.front.mall.service.IOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderService implements IOrderService {
    @Autowired
    private IOrderRepository orderRepository;

    @Resource
    private IGoodsRepository goodsRepository;

    @Autowired
    private IUserAddressRepository userAddressRepository;

    @Autowired
    private IOrderItemRepository orderItemRepository;

    @Override
    public OrderDetailVO detail(CurrentUser user, String orderNo) {
        //验证 订单号属于当前用户  List<OrderGoodsItemVO> orderGoodsItemVOList;
        OrderPO orderPO = orderRepository.getOrderByUserIdAndOrderNo(user.getUserId(),orderNo);
        if(orderPO!=null) {
            OrderDetailVO orderDetailVO = orderPO2OrderDetailVO(orderPO);
            List<OrderItemPO> orderItemPOS =
                    orderRepository.listOrderItemByOrderId(orderPO.getId());
            List<OrderGoodsItemVO> orderGoodsItemVOList = orderItemPOList2OrderGoodsItemVOList(orderItemPOS);
            orderDetailVO.setOrderGoodsItemVOList(orderGoodsItemVOList);
            orderDetailVO.setOrderLogVOS(mockOrderLogs());
            return orderDetailVO;
        }else{
            throw new ServiceException(ServiceCode.ERROR_BAD_REQUEST, "该用户暂无订单!!");
        }


    }

    private List<OrderLogVO> mockOrderLogs() {
        List<OrderLogVO> result = new ArrayList<>();
        OrderLogVO orderLogVO = new OrderLogVO();
        LocalDateTime localDateTime = LocalDateTime.now();
        orderLogVO.setTime(localDateTime);
        orderLogVO.setStatus(1);
        orderLogVO.setMessage("您提交了订单,请等待系统确认");
        result.add(orderLogVO);
        return result;
    }
    /**
     * A 数据存储
     * 订单数据放在哪？
     * 1 先放 redis 支付完成后，再放到数据库，
     * 先让redis，然后没有立即支付，通话半小说，redis挂了
     * 2 结论  直接放到数据库 为了保证订单数据不丢，ACID
     * <p>
     * 订单入参
     * 用户信息  选择收货地址
     * 商品信息  商品id,商品数量,商品单价
     * 配送信息  默认 老板跑着送
     * 支付信息  默认  老板直接收
     * 发票信息  营销信息 可选
     * <p>
     * B业务逻辑
     * 业务验证
     * 1 验证用户信息
     * 验证收货收货地址,通过用户id和地址id 去查询用户地址表 是否存在,获取具体的收货信息(地址,联系人,联系方式)
     * 2验证商品信息
     * 2.1 商品是否存在,是否上架,10个商品,某个商品被下架,给用户提示,不能给用户直接下单(不要给用户做决定)
     * 合理的提示, 到购物车检查,或者继续下单
     * 2.2 验证库存
     * 小电商暂时不用考虑库存,用户下完单,让供应商发货.
     * 预售 先忽悠用户下单,什么时候发货不确定.
     * 酒店的房间
     * 票 电影票和火车票 同一时间只能一个人定
     * 3计算价格 (订单服务要重新计算价格 基于商品最新的信息)
     * 价格中心服务 提供接口来计算价格,对计算逻辑进行封装,如果没有价格中心,商品服务可以提供
     * 核心的目标 只有一个地方来计算价格, 购物车/订单 来调用计算价格的接口
     * <p>
     * 4 生成订单
     * 订单表
     * 订单基本信息 id,订单编号(唯一) 后端生成 uuid 分布式id生成器,雪花算法,订单状态(都有什么状态),下单时间,更新时间
     * 用户信息  用户id,用户名称,用户收货地址,联系方式   可选 一个订单多件商品可以有多个收货人
     * 商品信息  商品id,商品名称,商品数量,商品单价 一个订单有多条商品信息
     * 支付信息  支付方式,支付时间,支付状态(都有什么状态),支付金额
     * 配送信息  配送方式,配送时间,配送状态(都有什么状态), 配送金额
     * 备注信息 多放肉,少放菜
     * <p>
     * 拆单  把一个订单拆分为多个订单
     * 1 一个订单多件商品可以有多个收货人
     * 2 配送方式不同  冰箱大件 和 海鲜 冷链
     * 订单快照  用户下单时此时此刻的信息拍个照片 保存起来,不能只存id,查的时候从别的服务里查.
     * C 数据存储
     * 1 生成订单的主表
     * OrderPO order = new OrderPO();
     * order 设置  用户信息 支付信息 配送信息  备注信息 订单状态(新创建) 下单时间
     * 2 生成商品列表
     * goodsAddParams 循环 OrderGoodsAddParam 转换为GoodsItemPO
     * 3 计算总价
     * <p>
     * 4 保存订单数据
     * 5 保存商品信息数据
     * 6 加个事务
     *
     * @param user
     * @param orderAddParam
     * @return
     */
    @Override
    public String createOrder(CurrentUser user, OrderAddParam orderAddParam) {
        //1 验证用户信息
        //2 验证商品信息
        //2.1 验证商品是否存在
        List<OrderGoodsAddParam> orderGoodsAddParams = orderAddParam.getGoodsAddParams();
        //重点记忆
        List<Long> goodsIds = orderGoodsAddParams
                .stream().map(OrderGoodsAddParam::getGoodsId)
                .collect(Collectors.toList());
        //批量判断商品id是否存在   具体商品信息
        List<GoodsPO> goodsPOS = goodsRepository.listGoodsByGoodsIds(goodsIds);
        log.debug("批量判断商品id是否存在,出参goodsPOS:{}", goodsPOS);
        if (CollectionUtils.isEmpty(goodsPOS)) {
            throw new ServiceException(ServiceCode.ERROR_BAD_REQUEST, "全部商品不存在!!");
        }

        if (!(orderGoodsAddParams.size() == goodsPOS.size())) {
            throw new ServiceException(ServiceCode.ERROR_BAD_REQUEST, "部分商品不存在!!");
        }


        //2.2 判断商品上否上架      todo 有问题
        //筛选出下架的商品
        List<GoodsPO> collect = goodsPOS.stream().filter(goodsPO -> (goodsPO.getIsPutOn() != null && goodsPO.getIsPutOn() == 0)).collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(collect)) {
            throw new ServiceException(ServiceCode.ERROR_BAD_REQUEST, "部分商品已下架!!");
        }

        //判断用户信息 用户存地址的id就行,把具体的地址存入具体的表中
        UserInfoParam userInfoParam = orderAddParam.getUserInfoParam();
        Integer addressId = userInfoParam.getAddress();

        //判断当前用户有没有这个地址
        UserAddressPO userAddressPO =
                userAddressRepository.getAddressByUserIdAndAddressId(user.getUserId(), addressId);
        log.debug("判断当前用户有没有这个地址,出参userAddressPO:{}", userAddressPO);
        if (userAddressPO == null) {
            throw new ServiceException(ServiceCode.ERROR_BAD_REQUEST, "用户收货地址无效,检查!!");
        }

        //计算价格
        //商品总价
        List<OrderItemPO> orderItemPOS = new ArrayList<>();
        final BigDecimal[] totalPrice = {BigDecimal.ZERO};
        final Integer totalGoodsNum[] = {0};
        Map<Long, GoodsPO> groupGoodsMap = goodsPOS
                .stream()
                .collect(Collectors.toMap(GoodsPO::getId, Function.identity()));

        orderGoodsAddParams.forEach(orderGoodsAddParam -> {
            //商品id 数量 价格
            Long goodId = orderGoodsAddParam.getGoodsId();
            Integer goodNum = orderGoodsAddParam.getGoodsNum();
            //价格需要从商品中拿,因为价格可能会改变
            //获取入参价格
            Long price = orderGoodsAddParam.getPrice();
            //获得真正的价格
            GoodsPO goodsPO = groupGoodsMap.get(goodId);
            BigDecimal salePrice = goodsPO.getSalePrice();
            String title = goodsPO.getTitle();
            //比较价格
            int compare = salePrice.compareTo(new BigDecimal(price));
            if (compare != 0) {
                throw new ServiceException(ServiceCode.ERROR_BAD_REQUEST, goodId.toString() + "号商品价格发生改变!!!");
            }

            //商品数量 * 商品价格
            BigDecimal multiply = salePrice.multiply(new BigDecimal(goodNum));
            totalPrice[0] = totalPrice[0].add(multiply);
            totalGoodsNum[0] = totalGoodsNum[0] + goodNum;
            //存入单个商品的信息
            OrderItemPO orderItemPO = bulidOrderItemPO(goodId, goodNum, salePrice,title);
            orderItemPOS.add(orderItemPO);
        });

        //准备商品主表数据
        OrderPO orderPO = bulidOrderPO(totalGoodsNum[0], totalPrice[0], user.getUserId());

        //创建订单表
        Integer orderRows = orderRepository.save(orderPO);

        if (orderRows != 1) {
            throw new ServiceException(ServiceCode.ERROR_BAD_REQUEST, "保存主订单失败");
        }
        //保存订单关联的商品信息
        orderItemPOS.forEach(orderItemPO -> {
            orderItemPO.setOrderId(orderPO.getId());
            //todo 建议使用批处理
            Integer orderItemRows = orderItemRepository.save(orderItemPO);
            if (orderItemRows != 1) {
                throw new ServiceException(ServiceCode.ERROR_BAD_REQUEST, "保存订单关联的商品信息失败");
            }
        });
        return orderPO.getOrderNo();
    }
    private OrderPO bulidOrderPO(Integer totalGoodsNum, BigDecimal totalPrice, Long userId) {
        OrderPO orderPO = new OrderPO();
        orderPO.setGoodsNum(totalGoodsNum);
        orderPO.setOrderNo(UUID.randomUUID().toString());
        orderPO.setTotalPrice(totalPrice);
        orderPO.setBuyerId(userId);
        orderPO.setOrderState(0);

        orderPO.setGmtCreate(new Date());
        return orderPO;
    }

    private OrderItemPO bulidOrderItemPO(Long goodId, Integer goodNum, BigDecimal salePrice,String title) {
        OrderItemPO orderItemPO = new OrderItemPO();
        orderItemPO.setGoodsId(goodId);
        orderItemPO.setGoodsNum(goodNum);
        orderItemPO.setSaleUnitPrice(salePrice);
        orderItemPO.setGoodsTitle(title);
        return orderItemPO;
    }
    @Override
    public PageData<OrderVO> list(CurrentUser user, Integer pageSize, Integer pageNum) {
        PageData<OrderPO> orderPOs = orderRepository.pageOrderByUserId(user.getUserId(), pageSize, pageNum);
        PageData<OrderVO> result = new PageData<>();
        //转换数据 PageData 中的 pageSize、total、currentPage、maxPage 是非泛型属性（类型固定为 Integer 或 Long），且在两个 PageData 中名称完全一致，所以 BeanUtils.copyProperties(orderPOs, result) 能直接复制这些属性，无需手动赋值。
        //list 是泛型属性（List<OrderPO> 和 List<OrderVO>），因类型不同，必须手动转换每个元素（OrderPO → OrderVO），否则会触发 ClassCastException。
//        BeanUtils.copyProperties(orderPOs,result);
//        List<OrderVO> list = result.getList();
//        for (OrderVO orderVO : list) {
//            System.out.println(orderVO);
//        }
        if (orderPOs != null && !CollectionUtils.isEmpty(orderPOs.getList())) {

            BeanUtils.copyProperties(orderPOs, result);
            //  转换数据列表：将 List<OrderPO> 逐个转为 List<OrderVO>
            //List<OrderVO> orderVOList = getOrderVOListRow(orderPOs);

            List<OrderVO> orderVOList = getOrderVOList(orderPOs);
            //  将转换后的 VO 列表设置到结果对象中（覆盖拷贝时的 PO 列表引用）
            result.setList(orderVOList);

        } else {

        }


        return result;

    }



    private List<OrderVO> getOrderVOList(PageData<OrderPO> orderPOs) {
        List<Long> orderIds = orderPOs.getList().stream().map(OrderPO::getId).collect(Collectors.toList());
        List<OrderItemPO> orderItemPOS = orderRepository.listOrderItemByOrderIds(orderIds);
        //把所以商品按照订单id分组
        Map<Long, List<OrderItemPO>> groupOrderItemByOrderId = orderItemPOS
                .stream()
                .collect(Collectors.groupingBy(OrderItemPO::getOrderId)); //

        List<OrderVO> orderVOList = orderPOs.getList().stream()
                .map(orderPO -> { // 遍历每个 OrderPO，转换为 OrderVO
                    OrderVO vo = new OrderVO();
                    // 复制单个 OrderPO 的属性到 OrderVO（属性名/类型匹配时直接拷贝）
                    BeanUtils.copyProperties(orderPO, vo);
                    // （可选）如果有 PO 和 VO 不同名的属性，需手动赋值
                    // 例如：vo.setOrderStatusDesc(convertStatus(po.getOrderStatus()));

                    //存入具体商品数据
                    List<OrderItemPO> itemPOS = groupOrderItemByOrderId.get(orderPO.getId());

                    //把这个数据存入对应的订单中
                    if(!CollectionUtils.isEmpty(itemPOS)) {
                        List<OrderGoodsItemVO> orderGoodsItemVOList = orderItemPOList2OrderGoodsItemVOList(itemPOS);
                        vo.setOrderGoodsItemVOList(orderGoodsItemVOList);
                    }

                    return vo;
                })
                .collect(Collectors.toList()); // 收集转换后的 OrderVO 为列表
        return orderVOList;
    }
    private List<OrderVO> getOrderVOListRaw(PageData<OrderPO> orderPOs) {
        List<OrderVO> orderVOList = orderPOs.getList().stream()
                .map(orderPO -> { // 遍历每个 OrderPO，转换为 OrderVO
                    OrderVO vo = new OrderVO();
                    // 复制单个 OrderPO 的属性到 OrderVO（属性名/类型匹配时直接拷贝）
                    BeanUtils.copyProperties(orderPO, vo);
                    // （可选）如果有 PO 和 VO 不同名的属性，需手动赋值
                    // 例如：vo.setOrderStatusDesc(convertStatus(po.getOrderStatus()));

                    //存入具体商品数据
                    Long id = orderPO.getId();
                    List<OrderItemPO> orderItemPOS = orderRepository.listOrderItemByOrderId(id);
                    //把这个数据存入对应的订单中
                    List<OrderGoodsItemVO> orderGoodsItemVOList = orderItemPOList2OrderGoodsItemVOList(orderItemPOS);
                    vo.setOrderGoodsItemVOList(orderGoodsItemVOList);
                    return vo;
                })
                .collect(Collectors.toList()); // 收集转换后的 OrderVO 为列表
        return orderVOList;
    }

    private List<OrderVO> getOrderVOListRow(PageData<OrderPO> orderPOs) {
        return  orderPOs.getList().stream()
                .map(orderPO -> { // 遍历每个 OrderPO，转换为 OrderVO
                    OrderVO vo = new OrderVO();
                    // 复制单个 OrderPO 的属性到 OrderVO（属性名/类型匹配时直接拷贝）
                    BeanUtils.copyProperties(orderPO, vo);
                    // （可选）如果有 PO 和 VO 不同名的属性，需手动赋值
                    // 例如：vo.setOrderStatusDesc(convertStatus(po.getOrderStatus()));

                    //存入具体商品数据   一个一个查
                    Long id = orderPO.getId();
                    List<OrderItemPO> orderItemPOS = orderRepository.listOrderItemByOrderId(id);
                    //把这个数据存入对应的订单中
                    List<OrderGoodsItemVO> orderGoodsItemVOList = orderItemPOList2OrderGoodsItemVOList(orderItemPOS);
                    vo.setOrderGoodsItemVOList(orderGoodsItemVOList);
                    return vo;
                })
                .collect(Collectors.toList()); // 收集转换后的 OrderVO 为列表
    }

    private List<OrderGoodsItemVO> orderItemPOList2OrderGoodsItemVOList(List<OrderItemPO> orderItemPOS) {

        return orderItemPOS
                .stream()
                .map(orderItemPO -> {return orderItemPO2OrderGoodsItemVO(orderItemPO);})
                .collect(Collectors.toList());
    }

    private OrderGoodsItemVO orderItemPO2OrderGoodsItemVO(OrderItemPO orderItemPO) {
        OrderGoodsItemVO orderGoodsItemVO = new OrderGoodsItemVO();
        BeanUtils.copyProperties(orderItemPO, orderGoodsItemVO);
        return orderGoodsItemVO;
    }

    private OrderDetailVO orderPO2OrderDetailVO(OrderPO orderPO) {
        OrderDetailVO orderDetailVO = new OrderDetailVO();
        BeanUtils.copyProperties(orderPO, orderDetailVO);
        return orderDetailVO;
    }

}
